package com.boat.framework.common.sequence;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;

import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.boat.framework.common.util.SpringUtils;

/**
 * 序列号数据库操作对象
 * 
 * @author 周江
 *
 */
public class SequenceStore {

	private static Logger logger = LoggerFactory.getLogger(SequenceStore.class);
	private static String TABLE_NAME = "boat.id_sequence";// db.table

	@Transactional(propagation = Propagation.REQUIRES_NEW)
	public long getSequenceValue(String seqName) {
		SqlSessionFactory sqlSessionFactory = SpringUtils.getBean("sqlSessionFactory");
		SqlSession sqlSession = sqlSessionFactory.openSession(false);
		Connection conn = sqlSession.getConnection();

		try {
			// openSession指定autoCommit=false不起作用, 只对通过session的方法执行的有效，只能自己再设置一次
			conn.setAutoCommit(false);
		} catch (SQLException e) {
			logger.error("关闭连接自动事务提交异常!");
		}

		try {
			Map<String, Long> resultMap = querySequence(conn, seqName);
			long seqValue = resultMap.get("seqValue");
			// 没找到，就新增sequence
			if (seqValue == -1) {
				insertSequence(conn, seqName);
				resultMap = querySequence(conn, seqName);
				seqValue = resultMap.get("seqValue");
			}

			updateSequence(conn, seqName, resultMap);

			conn.commit();
			return seqValue;
		} catch (SQLException sqlE) {
			logger.warn("取分段流水出现异常，流水名称：{}.", seqName, sqlE);
			throw new RuntimeException("obtain sequence error[" + seqName + "]");
		} finally {
			sqlSession.close();
		}
	}

	private void updateSequence(Connection connection, String seqName, Map<String, Long> resultMap)
			throws SQLException {
		PreparedStatement ps = null;
		try {
			ps = connection.prepareStatement("UPDATE " + TABLE_NAME
					+ " set SEQ_VALUE=?,UPDATE_TIME=CURRENT_TIMESTAMP WHERE SEQ_NAME=? AND SEQ_VALUE=?");

			long seqValue = resultMap.get("seqValue");
			long maxValue = resultMap.get("maxValue");
			long seqLen = resultMap.get("seqLen");
			long increment = resultMap.get("increment");
			long minValue = resultMap.get("minValue");

			long seqNextValue = seqValue + increment;
			if (seqNextValue >= maxValue || seqNextValue > (Math.pow(10, seqLen) - 1)) {
				seqNextValue = minValue;
			}

			ps.setLong(1, seqNextValue);
			ps.setString(2, seqName);
			ps.setLong(3, seqValue);
			int cnt = ps.executeUpdate();

			logger.debug("更新流水号[{}]，参数{}", seqName, seqNextValue);
			if (cnt != 1 || ps.getUpdateCount() != 1) {
				logger.error("更新流水号[{}]信息失败，返回更新记录数{}", seqName, cnt);
				throw new RuntimeException("update sequence error[" + seqName + "]");
			}
		} finally {
			if (ps != null) {
				ps.close();
			}
		}
	}

	private void insertSequence(Connection connection, String seqName) throws SQLException {
		PreparedStatement ps = null;
		try {
			ps = connection.prepareStatement("INSERT INTO " + TABLE_NAME
					+ " (SEQ_NAME,SEQ_VALUE,MIN_VALUE,MAX_VALUE,INCREMENT,SEQ_LEN,UPDATE_TIME) VALUES(?,?,?,?,?,?,CURRENT_TIMESTAMP)");

			ps.setString(1, seqName);
			ps.setLong(2, 1);
			ps.setInt(3, 1);
			ps.setLong(4, 999999);
			ps.setInt(5, 1);
			ps.setInt(6, 6);
			ps.execute();

			logger.debug("新增流水号[{}]，成功", seqName);

		} finally {
			if (ps != null) {
				ps.close();
			}
		}
	}

	private Map<String, Long> querySequence(Connection connection, String seqName) throws SQLException {
		PreparedStatement ps = null;
		ResultSet rs = null;
		Map<String, Long> resultMap = new HashMap<String, Long>();
		try {
			logger.debug("开始从数据库取分段流水[" + seqName + "]");
			ps = connection.prepareStatement("SELECT SEQ_VALUE, MAX_VALUE, SEQ_LEN, INCREMENT, MIN_VALUE " + " from "
					+ TABLE_NAME + " where SEQ_NAME=? ");
			ps.setString(1, seqName);
			rs = ps.executeQuery();
			if (rs.next()) {
				long seqValue = rs.getLong(1);
				long maxValue = rs.getLong(2);
				long seqLen = rs.getLong(3);
				long increment = rs.getLong(4);
				long minValue = rs.getLong(5);

				resultMap.put("seqValue", seqValue);
				resultMap.put("maxValue", maxValue);
				resultMap.put("seqLen", seqLen);
				resultMap.put("minValue", minValue);
				resultMap.put("increment", increment);

				logger.debug("取分段流水[" + seqName + "]完成");
				return resultMap;
			} else {
				resultMap.put("seqValue", -1L);
				return resultMap;
			}

		} finally {
			if (rs != null) {
				rs.close();// 释放数据库锁
			}
			if (ps != null) {
				ps.close();
			}
		}
	}

}
